package org.visionlibrary.image.filters.thresholding;

import javax.media.jai.TiledImage;

import org.visionlibrary.image.acquisition.ImageFactory;
import org.visionlibrary.image.model.AbstractFilter;
import org.visionlibrary.image.model.FilterTools;


/*
 * 1) Ridler T.W. and Calvard S. (1978) "Picture Thresholding Using an Iterative 
 * Selection Method" IEEE Trans. on System, Man and Cybernetics SMC-8(8): 630-632
 * 2) Trussell H.J. (1979) "Comments on Picture Thresholding Using an Iterative Selection 
 * Method" IEEE Trans. on Systems, Man, and Cybernetics, SMC-9(5): 311.
 */
public class SimpleIterativeSelectionThreshold extends AbstractFilter {
	protected double threshold = 127;
	protected double tolerance = 0.5d; /* iteration tolerance */

	protected int foreground = 0; // foreground pixel value (default 0)
	protected int background = 255; // background pixel value (default 255 for

	public SimpleIterativeSelectionThreshold() {
		this(0.5);
	}
	
	public SimpleIterativeSelectionThreshold(double tolerance) {
		this.tolerance = tolerance;
	}

	public SimpleIterativeSelectionThreshold(double tolerance, int foreground,
			int background) {
		this.tolerance = tolerance;
		this.foreground = foreground;
		this.background = background;
	}

	// 8bpc image)
	@Override
	public TiledImage applyFilter(TiledImage src, TiledImage dest) {
		if (null == src)
			throw new NullPointerException("Source image is null.");

		if (null == dest)
			dest = (new ImageFactory()).createCompatibleImage(src);

		int backPixelCount; /* number of background pixels at a given threshold */
		int forePixelCount; /* number of object pixels at a given threshold */
		int pixelCount; /* total number of pixels (background + object) */
		int backPixelSum; /* sum of background pixels at a given threshold */
		int forePixelSum; /* sum of object pixels at a given threshold */
		int pixelSum; /* total sum of the pixels */

		double oldThreshold;
		double newThresh;
		double backPixelMean; /*
							 * mean of the background pixels at a given
							 * threshold
							 */
		double forePixelMean; /* mean of the object pixels at a given threshold */
		double mean; /* mean gray-level in the image */

		int[] histo = FilterTools.getHistogram(src, 0);
		pixelCount = src.getWidth() * src.getHeight();

		pixelSum = 0;
		for (int ih = 0 + 1; ih < histo.length; ih++) {
			pixelSum += ih * histo[ih];
		}

		mean = pixelSum / (double) pixelCount;

		newThresh = mean;

		do {
			oldThreshold = newThresh;
			threshold = oldThreshold;
			// threshold = old_thresh + 0.5;

			backPixelSum = 0;
			backPixelCount = 0;
			for (int ih = 0; ih <= threshold; ih++) {
				backPixelSum += ih * histo[ih];
				backPixelCount += histo[ih];
			}
			backPixelMean = (backPixelCount == 0 ? 0.0
					: (backPixelSum / (double) backPixelCount));

			forePixelSum = pixelSum - backPixelSum;
			forePixelCount = pixelCount - backPixelCount;
			forePixelMean = (forePixelCount == 0 ? 0.0
					: (forePixelSum / (double) forePixelCount));

			newThresh = (backPixelMean + forePixelMean) / 2.0d;
		} while (Math.abs(newThresh - oldThreshold) > tolerance);

		int maxX = src.getWidth();
		int maxY = src.getHeight();
		for (int x = 0; x < maxX; x++)
			for (int y = 0; y < maxY; y++) {
				for (int ch = 0; ch < src.getNumBands(); ch++) {
					int pixel = src.getSample(x, y, ch);
					pixel = (pixel < threshold) ? foreground : background;
					dest.setSample(x, y, ch, pixel);
				}
			}

		return dest;
	}

}
